The three dimensions of IRP
IRPはRxを3つの直交する次元で一般化したものです。
IRP generalizes Rx over three orthogonal dimensions:
内在的識別子(Rx)と外在的識別子(IRP)の比較。すなわち、observable、observers、subscription、subjectなどのリアクティブエンティティは、もはやオブジェクトアドレスを用いて記述されるのではなく、明示的な識別子を用いて記述されることになります。
Intrinsic (Rx) versus extrinsic (IRP) identifiers, i.e. reactive entities such as observables, observers, subscriptions, and subjects are no longer described using object addresses but with explicit identifiers.
コード(Rx)とデータとしてのコード(IRP) リアクティブな計算の表現、すなわち、式木を使って意図を表現するために引用符を使用し、データとしてのコードの出荷を可能にします(IQbservable<T>を参照)。
Code (Rx) versus code-as-data (IRP) representations of reactive computations, i.e. using quotations to capture intent using expression trees, enabling shipping of code as data (cf. IQbservable<T>).
サブスクリプションのようなリアクティブなエンティティを作成または削除するための同期(Rx)と非同期(IRP)のAPI(Rxでは同期のSubscribeとDisposeメソッドを参照)。
Synchronous (Rx) versus asynchronous (IRP) APIs to create or delete reactive entities such as subscriptions (cf. Subscribe and Dispose methods which are synchronous in Rx).
これはサブスクリプションの引用表現(名前の Q を参照)であり、DisposeAsync のような非同期(Async 接頭辞を参照)操作をサポートします。IRP 準拠システムのクライアント API におけるこのインターフェースのイン スタンスは、外部の IRP 準拠サービスにおける定常的な反応型イベント処理計算(「サブスクリプ ション」)へのローカルなオブジェクト指向のプロキシタイプです。このプロキシを取得するためには、識別子を指定します。これらのプロキシのソースは、LINQ to SQLのDataContextに似ており、GetTable<T>コールを使用してテーブルへのプロキシを取得することができます。IRPの場合は,IReactiveProxyインターフェースが様々なGetメソッドを公開しています。
An example of a typical type in the IRP APIs is IAsyncReactiveQubscription, which is a quoted representation (cf. the Q in the name) of a subscription, supporting asynchronous (cf. the Async prefix) operations such as DisposeAsync. An instance of this interface in the client API of an IRP compliant system is a local object-oriented proxy type to a standing reactive event processing computation (a “subscription”) in some external IRP compliant service. In order to obtain this proxy, an identifier is specified. The source of these proxies is similar to the DataContext in LINQ to SQL where one can obtain proxies to tables using GetTable<T> calls. In the case of IRP, an IReactiveProxy interface exposes various Get methods:
code:C#
public interface IReactiveProxy
{
IAsyncReactiveQbservable<T> GetObservable<T>(Uri observableId);
IAsyncReactiveQbserver<T> GetObserver<T>(Uri observerId);
IAsyncReactiveQubject<T> GetStream<T>(Uri streamId);
IAsyncReactiveQubscription GetSubscription(Uri subscriptionId);
}
上のコードでは、ストリームやサブスクリプション・ファクトリなど、より高度な構造が省略されていますが、よく知られたサービスサイドの「リアクティブ・エンティティ」を参照するためにプロキシ・オブジェクトを返すという考え方は同じです。
Some more advanced constructs including stream and subscription factories have been omitted in the code fragment above, but the idea of returning proxy objects to refer to a well-known servie side “reactive entity” is the same.
識別子を表現するためにUri型を選択したのは、グラフ・データベースの取り組みの影響であると言えます。これは、schema.orgなどのオントロジーに応じて、Uri型の使用を最小限に抑えているからです。振り返ってみると、ここでは代わりに単純な文字列を使用することができました。実際、一部の機種のIRPのネイティブ実装では、エンティティの参照にGUIDを使用しており、COMオブジェクトとして実装することができます。サービスとクライアントデバイスシャドウの間の通信は、GUIDに変換されたURIを使用します。使用される識別子のタイプに応じてインターフェースをパラメータ化することが一時的に検討されました(TIdentifierを使用)が、これは全体的にジェネリックなパラメータの肥大化を伴う非常に不便なAPIにつながります。
The choice of Uri types to represent identifiers is arguably an influence of the graph database effortwherewewerestandardizingontheuseofURIstorepresentpropertiesonentitiesaccording to ontologies such as schema.org. In retrospect we could have used simple strings here instead. Actually, native implementations of IRP on some types of devices have used GUIDs to refer to entities, allowing them to be implemented as COM objects. Communication between services and client device shadows would use URIs which get translated to GUIDs. It was briefly considered to parameterize interfaces on the identifier type used (with a TIdentifier) but this leads to very unweildy APIs with generic parameter bloat all around.
Getメソッドを持つプロキシ・インターフェースは、LINQ to SQL APIとよく似ています。これは偶然ではなく、永続化されたデータストアに対するクエリではなく、ストリーミングクエリのオーサーに類似したエクスペリエンスを提供するための非常に意図的な選択です。この類似性の一環として、IRPは物理的/論理的に厳密なレイヤーを導入しています。論理的な空間では、IReactiveProxy(および後述の関連インターフェース)が実装階層のベースとなり、最終的にはLINQ to SQLのDataContextのような「コンテキスト」ベースのクラスにつながります。派生クラスは、例えば、リアクティブアーティファクトへの即時アクセスを提供するプロパティを公開することで、さらに特殊化することができます。
The proxy interface with its Get methods looks familiar to the LINQ to SQL APIs. This is not an accident; it’s a very deliberate choice to provide a similar experience to author streaming queries rather than queries over persisted data stores. As part of this analogy, IRP also introduces a strict physical/logical layering. In the logical space, the IReactiveProxy (and related interfaces, see later) form the base of the implementation hierarchy, which ultimately leads up to a “context” base class akin to DataContext in LINQ to SQL. Derived classes can further add specialization, for example by exposing properties that provide immediate access to reactive artifacts:
code:C#
public class MyContext : ClientContext
{
public IAsyncReactiveQbservable<WeatherInfo> Weather { get; } = base.GetObservable<WeatherInfo>(new Uri("reactor://platform.bing.com/weather"));
}
このようなコードは、Visual StudioのLINQ to SQLデザイナーのように、開発時にIRPに準拠したサービスから取得したメタデータからツールによって生成されるのが一般的です。KnownResourceの使用に注意してください。これについては、式木の正規化について説明するときに触れます。
Such code would typically be generated by tools from metadata that’s retrieved from an IRP-compliant service at development time, similar to the LINQ to SQL designer in Visual Studio. Note the use of KnownResource which we’ll get back to when talking about the normalization of expression trees.
IRPの3つの次元は、8つの組み合わせが可能な立方体を形成しています。クラシックRxには、多かれ少なかれ2つあります。1つ目はIObservable<T>で,内部識別子,同期型プログラミングモデル,コードベースの実装を備えています.2つ目はIQbservable<T>で,部分的に式ベースの実装を提供しています(observableの場合のみ).Rx APIの非同期型も考えられますが、これは何度もプロトタイプが作られています。このように、リアクティブエンティティに外部識別子を使用することが、IRPの主な差別化要因となっています。
The three dimensions of IRP form a cube with eight possible combinations. Classic Rx has two, more or less. The first is IObservable<T> which has intrinisic identifiers, a synchronous programming model, and a code-based implementation. The second is IQbservable<T> which provides a partial expression-based implementation (only for observables). One can envision asynchronous variants of the Rx APIs as well, which have been prototyped a number of times. This makes the use of extrinsic identifiers for reactive entities the main differentiator of IRP.
RxがIEnumerable<T>からIObservable<T>へのpull-to-pushの二重性から来ていることを考えると、IAsyncEnumerable<T>についても同じ基本的な二重性を発見し、結果としてIAsyncObservable<T>インターフェイスを作ることは全く理にかなっています。これらのインターフェースはIRPに存在し、様々な式木変換の際に使用されていますが、クエリ演算子の非同期実装はIRPの一部としては存在しません。しかし、「非同期Rx」の様々なプロトタイプには、そのような実装があります。IRPの場合、ユーザーの意図を問い合わせ評価エンジン内で実行される同期実行の問い合わせ演算子に変換しているため、そのような演算子の実装は必要ありませんでした。これについては後に詳しく説明します。これは将来的なイノベーションの可能性を秘めた領域です。
Given that Rx comes from the pull-to-push duality of IEnumerable<T> to IObservable<T>, it makes total sense to discover the same fundamental duality for IAsyncEnumerable<T> resulting in an IAsyncObservable<T> interface. These interfaces exist in IRP and are used during various expression tree transformations, but no asynchronous implementation of query operators exists as part of IRP. Various prototypes of “async Rx” do have such implementations though. In the case of IRP, we never had a necessity for such operator implementations because we translate user intent to synchronously executing query operators that run within a query evaluation engine, which we’ll describe in more detail later. This is an area of potential future innovation.